package com.hxzy.common.util.strategy;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.hxzy.common.enums.AccountTypeEnum;
import com.hxzy.common.enums.AckCode;
import com.hxzy.common.exception.ServiceException;
import com.hxzy.common.exception.ServiceStringException;
import com.hxzy.config.autoconfigure.passwordlock.PasswordLockProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * redis实现密码次数限定
 */
@Service
public class PwdErrorRedisLockStrategy implements PwdErrorLockStrategy{

    @Autowired
    private PasswordLockProperties  passwordLockProperties;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void lockAccount(String password, String hashPassword, Long id, AccountTypeEnum accountTypeEnum) {
        //密码错误次数的key   1小时过期
        String redisKeyPasswordError= passwordLockProperties.getRedisPasswordPrefix() + accountTypeEnum.name()+":"+id;
        //账户锁定的key      10分钟过期
        String redisKeyLocked=passwordLockProperties.getRedisLockedPrefix() + accountTypeEnum.name()+":"+id;

        //1、先判断账户是否被锁定
        if (this.redisTemplate.hasKey(redisKeyLocked)){
            throw new ServiceException(AckCode.PASSWORD_MAX_ERROR);
        }

        //2、判断redis是否存在key(有 (判断次数>限定的次数)  判断密码，错误了(次数+1，更新锁定时间) ，正确了，删除rediskey          没有)
        //判断密码是否正确
        if(!BCrypt.checkpw(password,hashPassword)){
            //向redis中增加错误次数
            Long increment = this.redisTemplate.opsForValue().increment(redisKeyPasswordError);
            //错误次数>最大错误次数
            if(increment>=this.passwordLockProperties.getMaxCount()){
                //删除错误记数key
                this.redisTemplate.delete(redisKeyPasswordError);

                //添加账户被锁定的时间
                this.redisTemplate.opsForValue().set(redisKeyLocked,"locked",this.passwordLockProperties.getLockedMinutes(), TimeUnit.MINUTES);
                //密码错误次数超过上限，账户将会被锁定10分钟
                String msg=StrUtil.format(AckCode.ACCOUT_LOCKED_TEN_MINUTES.getMsg(),this.passwordLockProperties.getLockedMinutes());
                throw new ServiceStringException(msg);
            }

            //如果是第1次错误，设定key过期时间
            if(increment.intValue()==1) {
                this.redisTemplate.expire(redisKeyPasswordError, this.passwordLockProperties.getErrorDurationHour(), TimeUnit.HOURS);
            }
            //密码错误，再输入错误{}次，锁户将会被锁定{}分钟
            String msg=StrUtil.format(AckCode.PASSWORD_ERROR_MAX.getMsg(),this.passwordLockProperties.getMaxCount()-increment,this.passwordLockProperties.getLockedMinutes());
            throw new ServiceStringException(msg);
        }

        //密码正确，删除rediskey
        this.redisTemplate.delete(redisKeyPasswordError);
    }
}
